package org.runecore.rs2.net;

import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import org.runecore.Launch;
import org.runecore.rs2.model.World;
import org.runecore.rs2.model.player.GameSession;

/**
 * RuneCore
 * Sep 4, 2011
 * @author Harry Andreas<Harry@RuneCore.org>
 */
public class NetworkHandler {
	
	/**
	 * The Server Socket
	 */
	private ServerSocketChannel serverSocket;
	
	/**
	 * The Channel Selector
	 */
	private Selector channelSelector;
	
	/**
	 * Constructor
	 */
	public NetworkHandler() {
		serverSocket = (ServerSocketChannel) Launch.getEnv().getAttribute("socketChannel");
		channelSelector = (Selector) Launch.getEnv().getAttribute("selector");
	}
	
	/**
	 * Apply's the network configurations
	 * i.e blocking etc
	 */
	public final void applyNetworkConfigurations()throws Exception {
		String ip = (String)Launch.getEnv().getAttribute("ip");
		int port = Integer.parseInt(((String)Launch.getEnv().getAttribute("port")));
		getServerSocket().configureBlocking(false);
		getServerSocket().socket().bind(new InetSocketAddress(ip, port));
		getServerSocket().register(getChannelSelector(), SelectionKey.OP_ACCEPT);
		Launch.getEnv().getLogger().info("Listening on "+ip+":"+port);
	}
	
	/**
	 * Called every 600ms
	 */
	public final void cycle() throws Exception {
		getChannelSelector().selectNow();
		for (SelectionKey sk : getChannelSelector().selectedKeys()) {
			if(sk.isAcceptable()) {
				acceptConnections();
			}
			if(sk.isReadable()) {
				World.getSingleton().getSessionMap().get(sk).handleInstream();
			}
		}
	}
	
	/**
	 * Accepts connections
	 */
	private final void acceptConnections() throws Exception {
		int maxClients = Integer.parseInt(((String)Launch.getEnv().getAttribute("accept-cycle")));
		SocketChannel accept;
		for(int i = 0; i < maxClients; i++) {
			accept = getServerSocket().accept();
			if(accept == null) {
				return;
			}
			accept.configureBlocking(false);
			SelectionKey key = accept.register(getChannelSelector(), SelectionKey.OP_READ);
			Launch.getEnv().getLogger().info("Accepted connection from "+accept.socket().getRemoteSocketAddress());
			GameSession session = new GameSession(key);
			World.getSingleton().getSessionMap().put(key, session);
		}
	}

	/**
	 * @return the serverSocket
	 */
	public ServerSocketChannel getServerSocket() {
		return serverSocket;
	}

	/**
	 * @return the channelSelector
	 */
	public Selector getChannelSelector() {
		return channelSelector;
	}

}
